#include "boxman.h"
#include "../common/win32_con_io.h"
#include <string.h>
const grid_t images_grid[] = {GEMPTY, GWALL, GTARGET, GMAN, GBOX};
const char images_chr[] = {' ', '#', 'o', 'a', 'x'};
const WORD images_attr[] = {0x00, 0x60, 0x10, 0x0f, 0x08};
const size_t NIMG = sizeof(images_chr);

grid_t map[HMAX][WMAX];
int height, width;
int xman, yman;

size_t nbox = 0, nbox_ontarget = 0;

grid_t lookup_grid(char img)
{
    int i;
    char hastarget = 0;
    if (isupper(img)){
        img = tolower(img);
        hastarget = 1;
    }
    for (i = 0; i < NIMG; i++){
        if (images_chr[i] == img){
            grid_t g = images_grid[i];
            return hastarget ? g | GTARGET : g;
        }
    }
    return INVALID;
}

char lookup_char(grid_t g)
{
    int i;
    if (g != GTARGET && g & GTARGET){
        g &= ~GTARGET;
    }
    for (i = 0; i < NIMG; i++){
        if (images_grid[i] == g){
            char c = images_chr[i];
            return toupper(c);
        }
    }
    return 0;
}

WORD lookup_attr(grid_t g)
{
    int i;
    WORD a = 0x00;
    for (i = 0; i < NIMG; i++)
        if (g & images_grid[i])
            a |= images_attr[i];
    if (!(a & 0x0f))
        a |= 0x07;
    return a;
}

int fillline(int line, char *s)
{
    int i;
    for (i = 0; s[i] && s[i] != '\n'; i++){
        if ((map[line][i] = lookup_grid(s[i])) == INVALID){
            return 1;
        }
        if (map[line][i] & GMAN){
            xman = line;
            yman = i;
        }else if (map[line][i] & GBOX){
            nbox++;
            if (map[line][i] & GTARGET){
                nbox_ontarget++;
            }
        }
        
    }
    return 0;
}

void fill_margin_info();

int readin(FILE *from)
{
    char line[WMAX + 1];
    fgets(line, WMAX + 1, from);
    if ((width = strlen(line) - 1) <= 0)
        return 1;
    fill_margin_info();
    if (line[width] != '\n')
        error("Map too long");
    if (fillline(0, line))
        return 1;
    height = 1;
    while (fgets(line, width + 2, from) != NULL){
        int len = strlen(line);
        if (len == 1) return 0;
        if (len != width + 1)
            error("Error in map; on line %d\n", height);
        if (fillline(height, line))
            return 1;
        height++;
    }
    return 0;
}

size_t b_width;
int left_margin;
int len_gamename;
int gamename_margin;

const char fmt_level[] = "Level %03d";
int level_margin;

const char fmt_step[] = "Step %03d";
int step_margin;

void fill_margin_info()
{
    if (!b_width)
        b_width = getWindowWidth();
    if ((left_margin = (b_width - width) / 2) < 0)
        left_margin = 0;
    len_gamename = strlen(gamename);
    if ((gamename_margin = (width - len_gamename) / 2 + left_margin) < 0)
        gamename_margin = 0;
    if ((level_margin = (width - 9) / 2 + left_margin) < 0)
        level_margin = 0;
    if ((step_margin = (width - 8) / 2 + left_margin) < 0)
        step_margin = 0;
}

int x, y;
void drawmap()
{
    int i, j;
    for (i = 0; i < height; i++){
        x = left_margin;
        for (j = 0; j < width; j++, x++){
            writeOneChar(lookup_char(map[i][j]), x, y);
            writeOneAttr(lookup_attr(map[i][j]), x, y);
        }
        y++;
    }
}

void drawall(int level, int step)
{
    char msg_level[20], msg_step[20];
    sprintf(msg_level, fmt_level, level);
    sprintf(msg_step, fmt_step, step);

    x = gamename_margin;
    y = 1;
    writeString(gamename, x, y);

    x = level_margin;
    y++;
    writeString(msg_level, x, y);
    
    x = step_margin;
    y++;
    writeString(msg_step, x, y);

    y += 2;

    drawmap();
}

void movemask(grid_t mask, int x0, int y0, int x1, int y1)
{
    map[x0][y0] &= ~mask;
    map[x1][y1] |= mask;
}

int moveman(int dx, int dy)
{
    int x1 = xman + dx;
    int y1 = yman + dy;
    grid_t g1 = map[x1][y1];
#if DEBUG
    printf("Trying to move man(%d, %d) by (%d, %d)\n", xman, yman, dx, dy);
#endif
    if (g1 == GWALL){
        return 0;
    }else if (g1 == GEMPTY || g1 == GTARGET){
        movemask(GMAN, xman, yman, x1, y1);
        xman = x1;
        yman = y1;
        return 1;
    }else if (g1 & GBOX){
        int x2 = x1 + dx;
        int y2 = y1 + dy;
        grid_t g2 = map[x2][y2];
#if DEBUG
        printf("A box encountered at (%d, %d); move it to (%d, %d)\n",
                x1, y1, x2, y2);
#endif
        if (g2 == GWALL){
            return 0;
        }else if (g2 == GEMPTY || g2 == GTARGET){
            movemask(GBOX, x1, y1, x2, y2);
            movemask(GMAN, xman, yman, x1, y1);
            xman = x1;
            yman = y1;
            if (g1 & GTARGET){
                nbox_ontarget--;
            }
            if (g2 == GTARGET){
                nbox_ontarget++;
            }
            return 1;
        }
    }
    return 0;
}
